使用pygame开发合金弹头(5)

您所在的位置:网站首页 合金弹头 音效 使用pygame开发合金弹头(5)

使用pygame开发合金弹头(5)

2024-07-10 21:14| 来源: 网络整理| 查看: 265

导读

Python的强大超出你的认知,Python的功能不止于可以做网络爬虫,数据分析,Python完全可以进行后端开发,AI,Python也可进行游戏开发,本文将会详细介绍Python使用pygame模块来开发一个名为“合金弹头”的游戏

合理绘制地图

前面开发已经完成了游戏中主要要求:各种怪物和角色,只是角色跑动的效果较差,这其实只是一个视觉效果:由于游戏的背景地图总是静止的,因此玩家会感觉角色似乎并未跑动。

为了让角色的跑动效果更加真实,游戏需要根据玩家跑动的位移来改变背景地图,当游戏的背景地图动起来之后,玩家控制的角色就似乎在地图上“跑”起来了。

为了集中处理游戏的界面绘制,程序在ViewManager类中定义了一个draw_game(self, screen, mm, player)方法,该方法负责整个游戏场景。该方法的实现思路就是先绘制游戏地图,然后所有的怪物,最后绘制绘制游戏角色即可。下面是draw_game()方法的代码。

代码语言:javascript复制 def draw_game(self, screen, mm, player): ''' 绘制游戏界面的方法,该方法先绘制游戏背景地图, 再绘制所有怪物,最后绘制游戏角色 ''' # 画地图 if self.map != None: width = self.map.get_width() + player.shift() # 绘制map图片,也就是绘制地图 screen.blit(self.map, (0, 0), (-player.shift(), 0, width, self.map.get_height())) total_width = width # 采用循环,保证地图前后可以拼接起来 while total_width < self.screen_width: map_width = self.map.get_width() draw_width = self.screen_width - total_width if map_width < draw_width: draw_width = map_width screen.blit(self.map, (total_width, 0), (0, 0, draw_width, self.map.get_height())) total_width += draw_width # 画角色 player.draw(screen) # 画怪物 mm.draw_monster(screen, self)

上面方法中第一行screen.blit(...)代码使用screnn的blit()方法来绘制背景位图,第二行screen.blit(...)代码依然使用了blit()方法来绘制背景位图——这是因为当角色在地图上不断地向右移动时,随着地图不断地向左拖动,地图就会不能完全覆盖屏幕右边,此时需要再绘制一张背景位图,这样才可以拼成完成的地图——这样就形成了无限循环的游戏地图。

由于ViewManager已经提供了draw_game()方法来绘制游戏界面,因此game_functions程序的update_screen()方法只要调用ViewManager已经提供了draw_game()方法即可。因此将game_functions程序的update_screen()方法改为如下形式。

代码语言:javascript复制# 处理更新游戏界面的方法 def update_screen(screen, view_manager, mm, player): # 随机生成怪物 mm.generate_monster(view_manager) # 处理角色的逻辑 player.logic(screen) # 如果游戏角色已死,判断玩家失败 if player.is_die(): print('游戏失败!') # 检查所有怪物是否将要死亡 mm.check_monster(view_manager, player) # 绘制背景图 # screen.blit(view_manager.map, (0, 0)) # 画角色 # player.draw(screen) # 画怪物 # mm.draw_monster(screen, view_manager) # 绘制游戏 view_manager.draw_game(screen, mm, player) #① # 更新屏幕显示,放在最后一行 pygame.display.flip()

上面程序中3行被注释的代码是之前绘制游戏背景图片、绘制角色、绘制怪物的代码,现在把这行代码删掉(或注释掉),改为调用ViewManager的draw_game()方法绘制游戏界面即可,如上程序中①号代码所示。

此时再运行程序将会看到非常好的跑动效果。

增加音效

现在游戏已经运行起来了,但整个游戏是安静无声,这还不够好,游戏应该增加背景音效,还应该为发射子弹、爆炸、打中目标增加各种音效,增加音效的游戏会更逼真。

pygame提供了pygame.mixer模块来播放音效,该模块下主要包含了两种播放音效的方式:

使用pygame.mixer的Sound类:每个Sound对象管理一个音效,该对象通常用于播放短暂的音效,比如射击音效、爆炸音效等。使用pygame.mixer.music子模块:该子模块通常用于播放游戏的背景音乐,该子模块提供了一个load()方法用于加载背景音乐,并提供了一个play()方法用于播放背景音乐。

为了给游戏增加背景音乐,修改metal_slug.py程序,在该程序中加载背景音乐、播放背景音乐即可。将metal_slug.py程序中run_game()方法改为如下形式。

代码语言:javascript复制def run_game(): # 初始化游戏 pygame.init() # 初始化混音器模块 pygame.mixer.init() # ① # 加载背景音乐 pygame.mixer.music.load('music/background.mp3') # ② # 创建ViewManager对象 view_manager = ViewManager() # 设置显示屏幕,返回Surface对象 screen = pygame.display.set_mode((view_manager.screen_width, view_manager.screen_height)) # 设置标题 pygame.display.set_caption('合金弹头') # 创建玩家角色 player = Player(view_manager, '孙悟空', MAX_HP) while(True): # 处理游戏事件 gf.check_events(screen, view_manager, player) # 更新游戏屏幕 gf.update_screen(screen, view_manager, mm, player) # 播放背景音乐 if pygame.mixer.music.get_busy() == False: pygame.mixer.music.play()

上面程序中①号代码初始化pygame的混音器模块;②号代码调用pygame.mixer.music子模块的load()方法加载背景音乐;最后一行代码则调用pygame.mixer.music子模块的play()方法播放背景音乐。

接下来程序同样使用ViewManager来管理游戏所用的发射、爆炸等各种音效,程序在ViewManager的构造器中增加如下代码。

代码语言:javascript复制# 管理图片加载和图片绘制的工具类 class ViewManager: # 加载所有游戏图片、声音的方法 def __init__ (self): ... self.Y_JUMP_MAX = self.screen_height * 50 / 100 # 使用list列表管理所有的音效 self.sound_effect = [] #① # load方法加载指定音频文件,并将被加载的音频添加到list列表中管理 self.sound_effect.append(pygame.mixer.Sound("music/shot.wav")) self.sound_effect.append(pygame.mixer.Sound("music/bomb.wav")) self.sound_effect.append(pygame.mixer.Sound("music/oh.wav"))

上面程序中①号代码创建了一个list列表,接下来程序将所有通过Sound加载的音效都保存到该list列表中,以后程序即可通过该list列表来访问这些音效。

接下来为Player发射子弹时添加音效,Player使用add_bullet()方法来发射子弹,因此程序应该在该方法最后添加如下一行即可。

代码语言:javascript复制 # 发射子弹的方法 def add_bullet(self, view_manager): ... self.left_shoot_time = MAX_LEFT_SHOOT_TIME # 播放射击音效 view_manager.sound_effect[0].play() # ①

上面程序中①号代码即可控制Player在发射子弹时播放射击音效。

此外还需要控制怪物死亡时播放对应的音效:当炸弹和飞机爆炸时,应该播放爆炸特效,当枪兵死时,应该播放惨叫特效。因此程序需要修改monster_manager的check_monster()函数(该函数用于检测怪物是否将要死亡),当该函数内的代码检测到怪物将要死亡时,程序增加播放音效的代码。

修改后的check_monster()函数代码如下。

代码语言:javascript复制# 检查怪物是否将要死亡的函数 def check_monster(view_manager, player): # 获取玩家发射的所有子弹 bullet_list = player.bullet_list # 定义一个del_list列表,用于保存将要死亡的怪物 del_list = [] # 定义一个del_bullet_list列表,用于保存所有将要被删除的子弹 del_bullet_list = [] # 遍历所有怪物 for monster in monster_list.sprites(): # 如果怪物是炸弹 if monster.type == TYPE_BOMB: # 角色被炸弹炸到 if player.is_hurt(monster.x, monster.end_x, monster.start_y, monster.end_y): # 将怪物设置为死亡状态 monster.is_die = True # 播放爆炸音效 view_manager.sound_effect[1].play() # ① # 将怪物(爆炸的炸弹)添加到del_list列表中 del_list.append(monster) # 玩家控制的角色的生命值减10 player.hp = player.hp - 10 continue # 对于其他类型的怪物,则需要遍历角色发射的所有子弹 # 只要任何一个子弹打中怪物,即可判断怪物即将死亡 for bullet in bullet_list.sprites(): if not bullet.is_effect: continue # 如果怪物被角色的子弹打到 if monster.is_hurt(bullet.x, bullet.y): # 将子弹设为无效 bullet.is_effect = False # 将怪物设为死亡状态 monster.is_die = True # 如果怪物是飞机 if monster.type == TYPE_FLY: # 播放爆炸音效 view_manager.sound_effect[1].play() # 如果怪物是人 if monster.type == TYPE_MAN: # 播放惨叫音效 view_manager.sound_effect[2].play() # 将怪物(被子弹打中的怪物)添加到del_list列表中 del_list.append(monster) # 将打中怪物的子弹添加到del_bullet_list列表中 del_bullet_list.append(bullet) # 将del_bullet_list包含的所有子弹从bullet_list中删除 bullet_list.remove(del_bullet_list) # 检查怪物子弹是否打到角色 monster.check_bullet(player) # 将已死亡的怪物(保存在del_list列表中)添加到die_monster_list列表中 die_monster_list.add(del_list) # 将已死亡的怪物(保存在del_list列表中)从monster_list中删除 monster_list.remove(del_list)

上面第①号代码之前,程序将代表炸弹的怪物的is_die设为True,这表明炸弹已死、即将爆炸,因此第①号代码播放了爆炸音效;程序第二段粗体字代码同样放在monster.is_die=True之后,这意味着程序先将代表飞机或枪兵(人)的怪物死亡状态,然后使用粗体字代码播放了对应的音效。

此时再次运行游戏将会听到游戏的背景音乐,当角色发射子弹、怪物被打死时都会产生相应的音效,此时游戏变得逼真多了。

现在游戏还剩一个小小的问题:游戏中玩家控制的角色居然是不死的,即使角色生命值变成了负数,玩家依然可以继续玩这个游戏,程序只是在控制台打印“游戏失败!”字样,这显然不是我们期望的效果,下面将开始解决这个问题。

增加游戏场景

当玩家控制的角色的生命值小于0时,此时应该显示游戏失败,本游戏虽然已经判断了游戏失败,但程序只是在控制台打印“游戏失败!”字样,这显然是不够的,此处考虑增加一个代表游戏失败的场景。

此外,正常游戏开始时,通常会显示游戏登录的场景,而不是直接开始游戏,因此本节将会为游戏增加游戏开始、游戏失败两个场景。

下面先修改game_functions.py程序,在该程序中定义三个代表不同场景的变量。

代码语言:javascript复制# 代表登录场景的常量 STAGE_LOGIN = 1 # 代表游戏场景的常量 STAGE_GAME = 2 # 代表失败场景的常量 STAGE_LOSE = 3

接下来该程序需要在check_events()函数中针对不同场景处理不同的事件:对于游戏登录和游戏失败的场景,游戏会在界面上显示按钮,因此程序主要负责处理游戏界面的鼠标点击事件。

在update_screen()函数中,程序则需要根据不同场景来绘制不同的界面。

下面是修改后的game_functions.py程序的代码。

代码语言:javascript复制import sys import pygame from player import * # 代表登录场景的常量 STAGE_LOGIN = 1 # 代表游戏场景的常量 STAGE_GAME = 2 # 代表失败场景的常量 STAGE_LOSE = 3 def check_events(screen, view_manager, player): ''' 响应按键和鼠标事件 ''' for event in pygame.event.get(): # 处理游戏退出(只有登录界面和失败界面才可退出) if event.type == pygame.QUIT and (view_manager.stage == STAGE_LOGIN \ or view_manager.stage == STAGE_LOSE): sys.exit() # 处理登录场景下的鼠标按下事件 if event.type == pygame.MOUSEBUTTONDOWN and view_manager.stage == STAGE_LOGIN: mouse_x, mouse_y = pygame.mouse.get_pos() if on_button(view_manager, mouse_x, mouse_y): # 开始游戏 view_manager.stage = STAGE_GAME # 处理失败场景下的鼠标按下事件 if event.type == pygame.MOUSEBUTTONDOWN and view_manager.stage == STAGE_LOSE: mouse_x, mouse_y = pygame.mouse.get_pos() if on_button(view_manager, mouse_x, mouse_y): # 将角色生命值恢复到最大 player.hp = MAX_HP # 进入游戏场景 view_manager.stage = STAGE_GAME # 处理登录场景下的鼠标移动事件 if event.type == pygame.MOUSEMOTION and view_manager.stage == STAGE_LOGIN: mouse_x, mouse_y = pygame.mouse.get_pos() if on_button(view_manager, mouse_x, mouse_y): # 如果鼠标在按钮上方移动,控制按钮绘制高亮图片 view_manager.start_image_index = 1 else: view_manager.start_image_index = 0 pygame.display.flip() # 处理游戏场景下按键被按下的事件 if event.type == pygame.KEYDOWN and view_manager.stage == STAGE_GAME: if event.key == pygame.K_SPACE: # 当角色的left_shoot_time为0时(上一枪发射结束),角色才能发射下一枪。 if player.left_shoot_time


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3